﻿#include "ZUartDriver.h"

#include <cmath>

ZUartDriver::ZUartDriver(uint32_t UART_DEVICE_ID,
                         uint32_t INTC_DEVICE_ID,
                         uint32_t UART_INT_IRQ_ID,
                         void (*dataDeal)(void *),
                         uint32_t baudRate,
                         uint8_t triggerLevel,
                         uint8_t priority
    )
  : UART_DEVICE_ID_(UART_DEVICE_ID),
    INTC_DEVICE_ID_(INTC_DEVICE_ID),
    UART_INT_IRQ_ID_(UART_INT_IRQ_ID),
    dataDeal_(dataDeal),
    baudRate_(baudRate),
    triggerLevel_(triggerLevel),
    priority_(priority),
    uartPs_(std::make_shared<XUartPs>()),
    intc_(std::make_shared<XScuGic>()) {
  if (triggerLevel_ == 0 || triggerLevel_ > 63) {
    // TODO: 加入日志打印
    throw std::invalid_argument("Invalid trigger level");
  }
  this->initialize();
}

void ZUartDriver::write(const std::vector<uint8_t> &data) const {
  this->write(const_cast<uint8_t *>(data.data()), data.size());
}

void ZUartDriver::write(uint8_t *data, uint32_t size) const {
  // XUartPs_Send发送大量数据时,会一直触发中断,报发送缓存满,所以使用XUartPs_SendByte
  for (uint32_t i = 0; i < size; ++i) {
    XUartPs_SendByte(uartPs_->Config.BaseAddress, data[i]);
  }

  /*
  // 使用write发送大量数据时,会一直触发中断,报发送缓存满
  // 一包数据的大小
  const uint32_t frameSize = 8;
  // 如果数据太多,则分包发送
  if (size > frameSize) {
    // 发送的包数
    int count = ceil(size * 1.0 / frameSize);
    // 剩余字节数
    uint32_t remainSize = size;
    // 循环发送
    for (int i = 0; i < count;) {
      // 如果发送忙碌则不发
      if (XUartPs_IsSending(uartPs_.get())) {
        continue;
      }
      XUartPs_Send(uartPs_.get(), data,
                   remainSize >= frameSize ? frameSize : remainSize);
      remainSize -= frameSize;
      ++i;
    }
  }
  */
}

void ZUartDriver::initialize() {
  this->initUartDevice();
  this->initInterrupts();
}

void ZUartDriver::initUartDevice() {
  auto uart_cfg = XUartPs_LookupConfig(UART_DEVICE_ID_);
  if (!uart_cfg) {
    throw std::logic_error{"UART device not found"};
  }
  auto status = XUartPs_CfgInitialize(uartPs_.get(), uart_cfg,
                                      uart_cfg->BaseAddress);
  if (status != XST_SUCCESS) {
    throw std::logic_error{"UART device config failed"};
  }

  // UART设备自检
  status = XUartPs_SelfTest(uartPs_.get());
  if (status != XST_SUCCESS) {
    throw std::logic_error{"UART device self test failed"};
  }

  // 设置工作模式:正常模式
  XUartPs_SetOperMode(uartPs_.get(), XUARTPS_OPER_MODE_NORMAL);
  // 设置波特率,默认115200
  XUartPs_SetBaudRate(uartPs_.get(), baudRate_);
  // 设置RxFIFO的中断触发等级,1~63(应该是一包数据,数值表示收到的字节)
  XUartPs_SetFifoThreshold(uartPs_.get(), triggerLevel_);
  // 设置接收超时
  XUartPs_SetRecvTimeout(uartPs_.get(), 8);
}

void ZUartDriver::initInterrupts() {
  //初始化中断控制器
  auto intc_cfg = XScuGic_LookupConfig(INTC_DEVICE_ID_);
  if (!intc_cfg) {
    throw std::logic_error{"uart INTC not found"};
  }
  auto status = XScuGic_CfgInitialize(intc_.get(), intc_cfg,
                                      intc_cfg->CpuBaseAddress);
  if (status != XST_SUCCESS) {
    throw std::logic_error{"uart INTC config failed"};
  }

  // 设置并打开中断异常处理功能
  Xil_ExceptionInit();
  Xil_ExceptionRegisterHandler(
      XIL_EXCEPTION_ID_INT,
      (Xil_ExceptionHandler) XScuGic_InterruptHandler,
      intc_.get()
      );
  Xil_ExceptionEnable();

  // 为中断设置中断处理函数
  XScuGic_Connect(intc_.get(), UART_INT_IRQ_ID_,
                  (Xil_ExceptionHandler) dataDeal_,
                  uartPs_.get());
  XScuGic_SetPriorityTriggerType(intc_.get(), UART_INT_IRQ_ID_, priority_, 1);
  // 设置UART的中断触发方式
  // XUARTPS_IXR_RXFULL,FIFO满触发
  // XUARTPS_IXR_RXEMPTY,FIFO空触发
  // XUARTPS_IXR_RXOVR,FIFO收到数据触发
  // XUARTPS_IXR_TOUT,超时
  XUartPs_SetInterruptMask(uartPs_.get(), XUARTPS_IXR_RXOVR | XUARTPS_IXR_TOUT);
  // 使能GIC中的串口中断
  XScuGic_Enable(intc_.get(), UART_INT_IRQ_ID_);
}

void ZUartDriver::interruptHandler(void *call_back_ref) {
  XUartPs *uart_instance_ptr = (XUartPs *) call_back_ref;
  u32 rec_data = 0;
  u32 isr_status;//中断状态标志

  //读取中断ID寄存器，判断触发的是哪种中断
  isr_status = XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress,
                               XUARTPS_IMR_OFFSET);
  isr_status &= XUartPs_ReadReg(uart_instance_ptr->Config.BaseAddress,
                                XUARTPS_ISR_OFFSET);

  //判断中断标志位RxFIFO是否触发
  if (isr_status & (u32) XUARTPS_IXR_RXOVR) {
    rec_data = XUartPs_RecvByte(XPAR_PS7_UART_0_BASEADDR);
    //清除中断标志
    XUartPs_WriteReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET,
                     XUARTPS_IXR_RXOVR);
  } else if (isr_status & (u32) XUARTPS_IXR_TOUT) {
    rec_data = XUartPs_RecvByte(XPAR_PS7_UART_0_BASEADDR);
    //清除中断标志
    XUartPs_WriteReg(uart_instance_ptr->Config.BaseAddress, XUARTPS_ISR_OFFSET,
                     XUARTPS_IXR_TOUT);
  }
  // XUartPs_SendByte(XPAR_PS7_UART_0_BASEADDR,rec_data);
}